home *** CD-ROM | disk | FTP | other *** search
- /*
- Routines to implement a message dispatcher
-
- Written by: Anumele D. Raja and Mary Chan
-
- Date: April 18, 1991
-
- Copyright @ 1991 Apple Computer, Inc.
- */
-
- #include <Events.h>
-
- #include "MessageDispatcher.h"
-
- #include "MultiThread.h"
-
- enum { kFalse, kTrue };
-
- #define kUsed 0
- #define kFree -1
- #define kMaxNumber 2147483647 // 2^31-1
-
- // Function declarations
-
- mMessage *MatchingMessage(unsigned long msgId,
- tid_type msgFrom, unsigned short msgCode);
- int *EnterInDispatchTable(unsigned long msgId,
- tid_type msgFrom, unsigned short msgCode, long timeOut, void (*complRout)(mMessage *));
- void DispatchComplRout(mMessage *msgPtr);
- void FreeDispatchTable(DispatchTable **disTblHdl);
- void ReturnAllMessages(MessageQueue **msgQHdl);
- DispatchTable **AllocateDispatchBlock(void);
-
- /* Externals:
-
- These are locations with in the 'mlti' resource in memory as part of
- the MultiThread.a file.
-
- Only way to access them using PC relative addressing is to declare them as function calls.
- */
-
- extern void messageQueueSize();
- extern void dispatchTblHdl();
- extern void messageQueueHdl();
-
- /*
-
- Logic:
-
- SplReceive:
-
- If there is no completion routine then
- If timeout value < 0 then
- If there is matching message in our queue then
- return with message
- else
- return 0
- else // timeout value >= 0 then
- If there is matching message in our queue then
- return with message
- else
- Loop till you get a message or time out expires using Ticks
-
- else // there is compl
- If timeout value < 0 then
- If there is matching message in our queue then
- Call the completion routine with message
- return 0
- else
- return 0
- else if timeout value >= 0 then
- If there is matching message in our queue then
- Call the completion routine with message
- return 0
- else (Described in more detail in the code)
- Enter into the dispatch table with time out value
- Activate the entry after checking the message queue
- return 0
-
- Setup table entry:
-
- If DispatchTblHdl = 0 Then
- Allocate a handle N entries of Dispatch table.
- Put mID, mCode, mFrom, and timeout in DispatchTblHdl
- Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
- else
- if there is a free entry then
- Put mID, mCode, mFrom, and timeout in DispatchTblHdl
- else
- Allocate another handle N entries of Dispatch table.
- Chain it to the first one
- Put mID, mCode, mFrom, and timeout in DispatchTblHdl
- return a pointer to the flag location
-
- ourComplRout:
-
- if there is no message then
- Go through table and count down timeouts
- if any count becomes zero then
- call completion routine with 0
- Flag the entry as free
- If there is a pending entry then
- Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
- else
- Search through all the entries to check if there is a matching entry
- If found then
- Call the completion routine corresponding to the match
- Flag the entry as free
- If there is a pending entry then
- Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
- else
- Queue up this message
- Issue a Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, ourComplRout)
-
- */
-
- mMessage *SpecialReceive(unsigned long msgId, tid_type msgFrom,
- unsigned short msgCode, long timeOut, void (*complRout)(mMessage *))
- {
- mMessage *msgPtr;
- unsigned long targetTime;
- MessageQueue *msgQueue;
-
- // Get pointer to the message queue
-
- msgQueue = **(MessageQueue ***)messageQueueHdl;
-
- // First check if there is a dispatch table allocated. If not allocate one
-
- if (!*(DispatchTable ***)dispatchTblHdl) { // No despatch table allocated
- if ((*(DispatchTable ***)dispatchTblHdl = AllocateDispatchBlock()) != 0) {
-
- // If successfully allocated start the Receives
-
- // There was no receive given so far. So issue a receive with a timout of 1 tick
-
- Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, DispatchComplRout);
- }
-
- }
-
- // If there is no completion routine specified in the receive call
- // it should work like a normal receive and check for a message
-
- if (complRout == 0) {
- if (timeOut < 0) {
- if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0)
- return msgPtr;
- else
- return 0;
- } else {
- if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0)
- return msgPtr;
- else {
- if (timeOut != 0)
- targetTime = TickCount() + timeOut;
- else
- targetTime = kMaxNumber;
- while (targetTime < TickCount()) {
- if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0)
- return msgPtr;
- }
- return 0;
- }
- }
- }
-
- // If There is a completion routine specified
-
- else { // There is completion routine
- if (timeOut < 0) {
- if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0) {
- complRout(msgPtr);
- return 0;
- }
- else {
- return 0;
- }
-
- } else {
-
- /*
- The following code might look strange but it is necessary because of the
- following condition.
-
- Assume a receive has come in with some selection.
-
- First we check if it is the message queue and if it is not we enter into
- the dispatch table.
-
- Just after we check the message queue and just before we activate the
- dispatch table entry the completion routine may get the message.
- It checks to see if there is an entry in the dispatch table.
- There won't be an entry because we are just about to enter it.
- So the completion routines puts the message into the message queue.
- We, in the main line, do not know that and return.
-
- To avoid missing a mesage we should check the message queue again after
- making an entry. This creates another problem. If we check the
- message queue after making an entry another message might which
- will be active the entry.
-
- To solve this problem we should lock out the dispatch table and the message
- queue from the time we check the message queue and to the time we
- activate an entry.
-
- We can't do it because completion routine has to either put a message into
- the queue or activate an entry in the table.
-
- The solution followed is simple:
-
- Just before looking for matching message in the message queue look
- for a free entry in the dispatch table and copy the message selection
- into it. Then get a pointer to the flag word.
-
- Copy the fMsgCount from the message queue into the flag word.
- Because an active entry in the dispatch table is assumed to be a flag
- of 0 this will leave it as an inactive entry. (When the message
- queue is initializd the fMsgCount is started with 1 which is a non
- zero value).
-
- If a matching message is not found in the message queue subtract the
- fMsgCount into the flag word. Because we are using register as the
- pointer to the flag word the compiler will generate an instruction
-
- SUB.L D0, (Ax)
-
- If the message count has not been incremented in the meantime the flag
- word becomes zero thus activating the entry. If a message did get into
- the queue after we had checked the message queue then the flag word
- will be non zero and still inactive.
- */
-
- register int *flagPtr;
-
- flagPtr = EnterInDispatchTable(msgId, msgFrom, msgCode, timeOut, complRout);
-
- if (flagPtr == 0)
- return (mMessage *)kFailedReceive;
-
- while (*flagPtr) { // Do as long as there is a new message coming in
- *flagPtr = msgQueue->fMsgCount;
- if ((msgPtr = MatchingMessage(msgId, msgFrom, msgCode)) != 0) {
- complRout(msgPtr);
- return 0;
- }
- *flagPtr -= msgQueue->fMsgCount; // subtract the message count
- }
- }
- }
- }
-
- /*
- Routine to look for matching message if any
- */
-
- mMessage *MatchingMessage(unsigned long msgId,
- tid_type msgFrom, unsigned short msgCode)
- {
- MessageQueue *msgQueue;
- mMessage *msgPtr;
- int i;
-
- msgQueue = **(MessageQueue ***)messageQueueHdl;
- if (msgQueue) {
- for (i = 0; i < *(int *)messageQueueSize; i++) {
- if (msgPtr = msgQueue->fMsgPtr[i]) {
- if ((msgId == OS_MATCH_ALL || msgPtr->mId == msgId) &&
- (msgFrom == OS_MATCH_ALL || msgPtr->mFrom == msgFrom) &&
- (msgCode == OS_MATCH_ALL || msgPtr->mCode == msgCode)) {
- msgQueue->fMsgPtr[i] = 0; // take the message out of the queue
- return msgPtr;
- }
- }
- }
- }
- return 0;
- }
-
- /*
- Routine to set up the dispatch table
- */
-
- int *EnterInDispatchTable(unsigned long msgId,
- tid_type msgFrom, unsigned short msgCode, long timeOut, void (*complRout)(mMessage *))
- {
- DispatchTable *disPtr;
- DispatchTable **disHdl;
- int i;
-
- if (disHdl = *(DispatchTable ***)dispatchTblHdl) { // If there is despatch table
- while (disHdl) {
-
- // Get pointer from the handle
-
- disPtr = (DispatchTable *)*disHdl;
-
- // Search through the array to find a free entry
-
- for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
- if (disPtr->disEntry[i].fFlag != kUsed) { // is this a free entry
- disPtr->disEntry[i].fMsgId = msgId;
- disPtr->disEntry[i].fMsgFrom = msgFrom;
- disPtr->disEntry[i].fMsgCode = msgCode;
- disPtr->disEntry[i].fTimeOut = timeOut;
- disPtr->disEntry[i].fComplRout = complRout;
- return &disPtr->disEntry[i].fFlag;
- }
- }
-
- // No free entry found try to check the next block
-
- if (!disPtr->fNext) { // If the next block is not yet allocated
- disPtr->fNext = AllocateDispatchBlock(); // allocate it and put handle in fNext
- disHdl = disPtr->fNext; // get the pointer (if allocation fails will quit)
- }
- else {
- disHdl = disPtr->fNext;
- }
- }
- }
- return 0;
- }
-
- // Completion routine for the dispatcher
-
- void DispatchComplRout(mMessage *msgPtr)
- {
- DispatchTable *disPtr;
- int i;
- MessageQueue *msgQueue;
- unsigned long msgId;
- tid_type msgFrom;
- unsigned short msgCode;
-
- disPtr = **(DispatchTable ***)dispatchTblHdl;
-
- // If the message pointer is zero then time out messages
-
- if (!msgPtr) {
- while (disPtr) {
- for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
- if (disPtr->disEntry[i].fFlag == kUsed) { // is this an active entry
- if (disPtr->disEntry[i].fTimeOut != kMaxNumber) { // there is a time out
- if (--disPtr->disEntry[i].fTimeOut <= 0) { // decrement timeout and
- if (disPtr->disEntry[i].fComplRout == 0) // verify completion routine is ther
- Debugger();
- else
- disPtr->disEntry[i].fComplRout(0); // if zero call completion routine with 0
- disPtr->disEntry[i].fFlag = kFree;
- }
- }
- }
- }
- if (disPtr->fNext)
- disPtr = *disPtr->fNext;
- else
- disPtr = 0;
- }
- }
-
- // If the message pointer is not zero then check if there is someone waiting
- // in the Dispatch table
-
- else {
- while (disPtr && msgPtr) {
- for (i = 0; i < kNoOfDispatchEntriesInBlock && msgPtr; i++) {
- if (disPtr->disEntry[i].fFlag == kUsed) { // is this an active entry
- msgId = disPtr->disEntry[i].fMsgId;
- msgFrom = disPtr->disEntry[i].fMsgFrom;
- msgCode = disPtr->disEntry[i].fMsgCode;
- if ((msgId == OS_MATCH_ALL || msgPtr->mId == msgId) &&
- (msgFrom == OS_MATCH_ALL || msgPtr->mFrom == msgFrom) &&
- (msgCode == OS_MATCH_ALL || msgPtr->mCode == msgCode)) {
- if (disPtr->disEntry[i].fComplRout == 0)
- Debugger();
- else
- disPtr->disEntry[i].fComplRout(msgPtr); // if zero call completion routine with 0
- msgPtr = 0; // message passed on
- disPtr->disEntry[i].fFlag = kFree;
- }
- }
- }
-
- // Try the next Dispatch Table block
-
- if (disPtr->fNext)
- disPtr = *disPtr->fNext;
- else
- disPtr = 0;
- }
-
- // There was non one waiting for this message so put it in the message queue
-
- if (msgPtr) { // message not passed on
- msgQueue = **(MessageQueue ***)messageQueueHdl;
- if (msgQueue) {
- for (i = 0; i < *(int *)messageQueueSize && msgPtr; i++) {
- if (!msgQueue->fMsgPtr[i]) {
- msgQueue->fMsgCount++; // Increment message count
- msgQueue->fMsgPtr[i] = msgPtr;
- msgPtr = 0;
- }
- }
- }
-
- if (msgPtr) { // there is no space to put the message in queue
- tid_type temp;
- temp = msgPtr->mFrom;
- msgPtr->mFrom = msgPtr->mTo;
- msgPtr->mTo = temp;
- msgPtr->mCode |= 0x8000;
- msgPtr->mStatus = 0x8000; // return message as undeliverable
- Send(msgPtr); // Which send does it use (the current one)
- }
- }
- }
-
- // Issue another receive with completion
-
- Receive(OS_MATCH_ALL, OS_MATCH_ALL, OS_MATCH_ALL, 1, DispatchComplRout);
- }
-
- // Free dispatch table in resonse to CloseDispatch
-
- void FreeDispatchTable(DispatchTable **disTblHdl)
- {
- DispatchTable *disPtr;
- int i;
-
- if ((*disTblHdl)->fNext) {
- FreeDispatchTable((*disTblHdl)->fNext); // Free Dispatch table recursively
- }
-
- // First set all entries to be free so that completion routine does not use them
-
- disPtr = *disTblHdl;
- for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
- disPtr->disEntry[i].fFlag = kFree;
- }
- HUnlock((Handle)disTblHdl);
- DisposHandle((Handle)disTblHdl);
- }
-
- // Free all messages in the message queue
-
- void ReturnAllMessages(MessageQueue **msgQHdl)
- {
- MessageQueue *msgQueue;
- int i;
-
- msgQueue = *msgQHdl;
- if (msgQueue) {
- for (i = 0; i < *(int *)messageQueueSize; i++) {
- if (msgQueue->fMsgPtr[i]) {
- FreeMsg(msgQueue->fMsgPtr[i]);
- }
- }
- }
- HUnlock((Handle)msgQHdl);
- DisposHandle((Handle)msgQHdl);
- }
-
- // Allocate a handle for dispatch table
-
- DispatchTable **AllocateDispatchBlock(void)
- {
- Handle newHdl;
- DispatchTable *disPtr;
- int i;
-
- newHdl = NewHandle(sizeof(DispatchTable));
-
- if (newHdl) {
- MoveHHi(newHdl);
- HLock(newHdl);
-
- // Set all entries to Free
-
- disPtr = *(DispatchTable **)newHdl;
-
- for (i = 0; i < kNoOfDispatchEntriesInBlock; i++) {
- disPtr->disEntry[i].fFlag = kFree;
- }
- disPtr->fNext = 0; // next block is not there
- }
-
- return (DispatchTable **)newHdl;
- }
-
- // Open Dispatch (the size of the message queue is passed as the parameter)
-
- void OpenDispatch(int queueSize)
- {
- Handle msgQHdl;
- MessageQueue *msgQueue;
- int i;
-
- *(int *)messageQueueSize = queueSize;
- *(Handle *)messageQueueHdl = msgQHdl = NewHandle(queueSize*sizeof(MessageQueue)+sizeof(int));
-
- if (msgQHdl) {
- MoveHHi(msgQHdl);
-
- HLock(msgQHdl);
-
- msgQueue = *(MessageQueue **)msgQHdl;
- for (i = 0; i < queueSize; i++) // clear all the message pointers in queue
- msgQueue->fMsgPtr[i] = 0;
-
- msgQueue->fMsgCount = 1;
- }
-
- SetSpecialReceive();
- }
-
- // Close Dispatch
-
- void CloseDispatch()
- {
- DispatchTable **disHdl;
- MessageQueue **msgQHdl;
-
- KillReceive(); // Kill Receive
-
- RestoreOriginalReceive();
-
- if (disHdl = *(DispatchTable ***)dispatchTblHdl) { // If there is despatch table
- FreeDispatchTable(disHdl);
- if (msgQHdl = *(MessageQueue ***)messageQueueHdl) {
- ReturnAllMessages(msgQHdl);
- }
- }
- }
-